package com.ccp.dev.workflow.service;

import com.ccp.dev.core.basic.base.BaseService;
import com.ccp.dev.core.basic.util.UUIDUtils;
import com.ccp.dev.core.util.BeanUtils;
import com.ccp.dev.core.util.StringUtil;
import com.ccp.dev.form.dao.FormRightsDao;
import com.ccp.dev.form.dao.FormTableDao;
import com.ccp.dev.form.model.FormTable;
import com.ccp.dev.workflow.bpmconst.BpmConst;
import com.ccp.dev.workflow.dao.ActDefModelDao;
import com.ccp.dev.workflow.dao.BpmNodeSetDao;
import com.ccp.dev.workflow.model.ActDefModel;
import com.ccp.dev.workflow.model.BpmNodeSet;
import com.ccp.dev.workflow.model.FlowNodeInfo;
import com.ccp.dev.workflow.model.NodeCache;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ClassDescribe:流程定义节点 Service
 * @author :jiayueqiao
 * Date: 2019-05-15
 * Since:1
 */
@Service
public class BpmNodeSetService extends BaseService<BpmNodeSet> {

    @Resource
    private BpmNodeSetDao bpmNodeSetDao;
    @Resource
    private ActDefModelDao actDefModelDao;
    @Resource
    private FormRightsDao formRightsDao;
    @Resource
    private FormTableDao formTableDao;
    /**
     * 根据act定义id 获取节点数据，并转换为map。
     * @param procDefId 模型定义id
     * @return 节点设置信息集合
     */
    public Map<String, BpmNodeSet> getMapByProcDefId(String procDefId){
        List<BpmNodeSet> list= bpmNodeSetDao.queryByProcDefId(procDefId);
        Map<String, BpmNodeSet> map= new HashMap<>(list.size());
        for(BpmNodeSet bpmNodeSet:list){
            map.put(bpmNodeSet.getNodeId(), bpmNodeSet);
        }
        return map;
    }

    /**
     * 根据act定义id 和父流程定义id获取节点数据，并转换为map。
     * @param procDefId 流程定义id
     * @param parentProcDefId 父流程定义id
     * @param isAll true :查询包括全局和业务表单的bpmNodeSet ，false:只是任务节点)
     * @return 节点信息map集合
     */
    public Map<String, BpmNodeSet> getMapByProcDefIdAndParentProcDefId(String procDefId, String parentProcDefId, boolean isAll) {
        List<BpmNodeSet> list = queryByProcDefIdAndParentProcDefId(procDefId, parentProcDefId , isAll);
        Map<String, BpmNodeSet> map= new HashMap<>(list.size());
        for(BpmNodeSet bpmNodeSet:list){
            map.put(bpmNodeSet.getNodeId(), bpmNodeSet);
        }
        return map;
    }

    /**
     * 根据定义id查询节点
     * @param procDefId 定义id
     * @return 节点设置信息集合
     */
    public List<BpmNodeSet> queryByProcDefId(String procDefId){
        return bpmNodeSetDao.queryByProcDefId(procDefId);
    }

    /**
     * 根据parentProcDefId和defId获取流程节点数据。
     * @param procDefId 流程定义id
     * @param parentProcDefId 父流程定义id
     * @param isAll (true :查询包括全局和业务表单的bpmNodeSet ，false:只是任务节点)
     * @return 节点设置信息集合
     */
    public List<BpmNodeSet> queryByProcDefIdAndParentProcDefId(String procDefId, String parentProcDefId,boolean isAll){
        Map<String,Object> params= new HashMap<>(3);
        params.put("procDefId", procDefId);
        params.put("parentProcDefId", parentProcDefId);
        if (isAll) {
            params.put("isAll", "isAll");
        }
        return bpmNodeSetDao.queryByProcDefIdAndParentProcDefId(params);
    }

    /**
     * 根据父流程定义查询所有的外部子流程
     * @param parentProcDefId 父流程定义id
     * @return 节点设置信息集合
     */
    List<BpmNodeSet> queryByParentProcDefId(String parentProcDefId){
        return bpmNodeSetDao.queryByParentProcDefId(parentProcDefId);
    }

    /**
     * 根据流程定义id和父流程定义id删除节点
     * @param procDefId 流程定义id
     * @param parentProcDefId 父流程定义id
     */
    @Transactional(rollbackFor = Exception.class)
    public void delByProcDefIdAndParentProcDefId(String procDefId, String parentProcDefId){
        Map<String, Object> params = new HashMap<>(2);
        params.put("procDefId", procDefId);
        params.put("parentProcDefId", parentProcDefId);
        bpmNodeSetDao.delByProcDefIdAndParentProcDefId(params);
    }

    /**
     * 根据定义id和节点id删除节点
     * @param nodeId 节点id
     * @param procDefId 定义id
     */
    @Transactional(rollbackFor = Exception.class)
    public void delByProcDefIdNodeId(String nodeId, String procDefId){
        Map<String, Object> params = new HashMap<>(2);
        params.put("nodeId", nodeId);
        params.put("procDefId", procDefId);
        bpmNodeSetDao.delByProcDefIdNodeId(params);
    }

    /**
     * 根据子流程节点id和子流程定义id，查找父流程中的节点
     * @param nodeId 节点id
     * @param procDefId 定义id
     * @return 节点设置信息集合
     */
    public List<BpmNodeSet> queryParentByProcDefIdAndNodeId(String nodeId, String procDefId){
        Map<String, Object> params = new HashMap<>(2);
        params.put("nodeId", nodeId);
        params.put("procDefId", procDefId);
        return bpmNodeSetDao.queryParentByProcDefIdAndNodeId(params);
    }

    /**
     * 根据子流程定义id获取所有的父流程定义id
     * @param procDefId 定义id
     * @return 节点设置信息集合
     */
    public List<BpmNodeSet> queryParentIdByProcDefId(String procDefId){
        return bpmNodeSetDao.queryParentIdByProcDefId(procDefId);
    }

    /**
     * 根据流程定义id和流程节点id，获取该节点的设置信息
     * @param nodeId 节点信息
     * @param procDefId 流程定义id
     * @param parentProcDefId 父流程定义id
     * @return 节点设置信息
     */
    public BpmNodeSet getByProcDefIdNodeIdOrParentProcDefId(String nodeId, String procDefId,String parentProcDefId) {
        Map<String, String> params = new HashMap<>(3);
        params.put("nodeId", nodeId);
        params.put("procDefId", procDefId);
        params.put("parentProcDefId", parentProcDefId);
        return bpmNodeSetDao.getByProcDefIdNodeIdOrParentProcDefId(params);
    }

    /**
     * 根据流程定义id和表单类型查询默认表单和起始表单
     * @param map 查询条件
     * @return 起始表单和默认表单
     */
    public BpmNodeSet getBySetType(Map<String, Object> map) {
        return bpmNodeSetDao.getBySetType(map);
    }

    /**
     * 根据流程定义id和表单类型查询默认表单和起始表单
     * @param procDefId 流程定义id
     * @param setType 值为（2，全局表单，3 流程业务表单）
     * @return 起始表单和默认表单
     */
    public BpmNodeSet getBySetType(String procDefId, Short setType) {
        Map<String, Object> params = new HashMap<>(2);
        params.put("procDefId", procDefId);
        params.put("setType", setType);
        return bpmNodeSetDao.getBySetType(params);
    }

    /**
     * 根据流程定义id，父流程定义id和表单类型查询默认表单和起始表单
     * @param procDefId 流程定义id
     * @param setType 表单类型
     * @param parentProcDefId 父流程定义id
     * @return 默认和起始表单设置
     */
    public BpmNodeSet getBySetType(String procDefId, Short setType, String parentProcDefId) {
        Map<String, Object> params = new HashMap<>(3);
        params.put("procDefId", procDefId);
        params.put("setType", setType);
        params.put("parentProcDefId", parentProcDefId);
        return bpmNodeSetDao.getBySetType(params);
    }

    /**
     * 保存节点配置信息
     * @param procDefId 流程定义id
     * @param nodeList 节点列表
     * @param parentProcDefId 父流程定义id
     */
    @Transactional(rollbackFor = Exception.class)
    public void save(String procDefId, List<BpmNodeSet> nodeList, String parentProcDefId) {
        if (StringUtil.isEmpty(parentProcDefId)) {
            delByStartGlobalDefId(procDefId, null);
        }else {
            delByStartGlobalDefId(procDefId, parentProcDefId);
        }

        for (BpmNodeSet nodeSet : nodeList) {
            if (nodeSet.getSetId() == null) {
                nodeSet.setSetId(UUIDUtils.getUUIDFor32());
                bpmNodeSetDao.add(nodeSet);
            }else {
                bpmNodeSetDao.update(nodeSet);
                // 在线表单
                if (nodeSet.getFormType() == 0) {
                    if (StringUtil.isNotEmpty(nodeSet.getOldFormKey())) {
                        if (nodeSet.getFormKey().equals(nodeSet.getOldFormKey())) {
                            continue;
                        }
                        // 原来定义过表单权限，并且新定义的在线表单和原定义的表单不一致
                        if (StringUtil.isEmpty(parentProcDefId)) {
                            formRightsDao.delByActDefIdAndNodeId(nodeSet.getProcDefId(), nodeSet.getNodeId(),0);
                        }else{
                            formRightsDao.delByActDefIdAndNodeIdAndParentActDefId(nodeSet.getProcDefId(), nodeSet.getNodeId(), parentProcDefId,0);
                        }
                    }
                }
            }
        }
        List<FormTable> list = formTableDao.getByProcDefId(procDefId);
        if (list.size() > 1) {
            throw new RuntimeException("流程定义只能对应一个表!");
        }
    }

    /**
     * 根据流程定义id删除节点设置信息
     * @param procDefId 流程定义id
     * @param parentProcDefId 父流程定义id
     */
    @Transactional(rollbackFor = Exception.class)
    public void delByStartGlobalDefId(String procDefId, String parentProcDefId) {
        Map<String, Object> params = new HashMap<>(2);

        params.put("procDefId", procDefId);
        if (StringUtil.isNotEmpty(parentProcDefId)) {
            params.put("parentProcDefId", parentProcDefId);
        }

        bpmNodeSetDao.delByStartGlobalDefId(params);
    }

    public List<BpmNodeSet> getByDefId(Map<String, Object> map) {
        return bpmNodeSetDao.queryByProcDefIdAndParentProcDefId(map);
    }

    /**
     * 通过定义ID及节点Id更新isJumpForDef字段的设置
     * @param nodeId 节点id
     * @param procDefId 流程定义id
     * @param isJumpForDef 是否正常跳转
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateIsJumpForDef(String nodeId, String procDefId, Short isJumpForDef) {
        Map<String,Object> params= new HashMap<>(3);
        params.put("isJumpForDef", isJumpForDef);
        params.put("nodeId", nodeId);
        params.put("procDefId", procDefId);
        bpmNodeSetDao.updateIsJumpForDef(params);
    }

    /**
     * 通过流程发布ID及节点id获取流程设置节点实体,如果未设置表单，则取全局表单设置
     * @return BpmNodeSet
     */
    public BpmNodeSet getByProcDefIdNodeId(String procDefId, String nodeId,String parentProcDefId) {

        Map<String,String> map = new HashMap<>(3);
        map.put("procDefId",procDefId);
        map.put("nodeId",nodeId);
        if(!StringUtil.isEmpty(parentProcDefId)){
            map.put("parentProcDefId",parentProcDefId);
        }
        BpmNodeSet bpmNodeSet = bpmNodeSetDao.getByProcDefIdNodeIdOrParentProcDefId(map);
        ActDefModel actDefModel = actDefModelDao.getByProcDefId(procDefId);
        //节点未设置表单取全局表单
        if (isFormEmpty(bpmNodeSet) ) {
            BpmNodeSet globalNodeSet = getBySetType(actDefModel.getProcDefId(),BpmNodeSet.SETTYPE_GLOABLEFORM, parentProcDefId);
            if(BeanUtils.isNotEmpty(globalNodeSet)){
                globalNodeSet.setIsHideOption(bpmNodeSet.getIsHideOption());
                globalNodeSet.setIsHidePath(bpmNodeSet.getIsHidePath());
                globalNodeSet.setInformType(bpmNodeSet.getInformType());
                globalNodeSet.setCanUploadFile(bpmNodeSet.getCanUploadFile());
                globalNodeSet.setIsAllowMobile(bpmNodeSet.getIsAllowMobile());
                globalNodeSet.setIsJumpForDef(bpmNodeSet.getIsJumpForDef());
                globalNodeSet.setJumpType(bpmNodeSet.getJumpType());
                globalNodeSet.setIsRequired(bpmNodeSet.getIsRequired());
                globalNodeSet.setIsPopup(bpmNodeSet.getIsPopup());
                globalNodeSet.setOpinionField(bpmNodeSet.getOpinionField());
                //增加分发汇总信息的赋值
                globalNodeSet.setJoinTaskKey(bpmNodeSet.getJoinTaskKey());
                globalNodeSet.setJoinTaskName(bpmNodeSet.getJoinTaskName());
                globalNodeSet.setNodeType(bpmNodeSet.getNodeType());
                return globalNodeSet;
            }
        }
        return bpmNodeSet;
    }

    public BpmNodeSet getByProcDefIdNodeId(String procDefId, String nodeId) {
        return getByProcDefIdNodeId(procDefId,nodeId,null);
    }


    /**
     * 判断表单是否为空。
     * @param bpmNodeSet 节点设置
     * @return boolean
     */
    private boolean isFormEmpty(BpmNodeSet bpmNodeSet) {
        short formType = bpmNodeSet.getFormType();
        String formKey = bpmNodeSet.getFormKey();
        // 没有设置表单的情况
        if (formType == -1) {
            return true;
        }
        // 在线表单的情况
        if (formType == 0) {
            if (StringUtil.isEmpty(formKey)) {
                return true;
            }
        }
        // url表单的情况。
        else {
            String formUrl = bpmNodeSet.getFormUrl();
            if (StringUtil.isEmpty(formUrl)) {
                return true;
            }
        }
        return false;
    }

    public List<BpmNodeSet> queryByDefIdOpinion(String procDefId){
        return bpmNodeSetDao.queryByDefIdOpinion(procDefId);
    }


    public List<BpmNodeSet> queryByParentDefIdOpinion(String procDefId,String parentProcDefId){
        return bpmNodeSetDao.queryByParentDefIdOpinion(procDefId, parentProcDefId);
    }

    /**
     * 获取节点设置人员选择器的范围
     * @param nodeId 节点id
     * @param procDefId 流程定义id
     * @param parentProcDefId 父流程定义id
     * @return BpmNodeSet
     */
    public BpmNodeSet getScopeByNodeIdAndProcDefId(String nodeId,String procDefId,String parentProcDefId) {
        Map<String,Object> params=new HashMap<>(3);
        params.put("nodeId", nodeId);
        params.put("procDefId", procDefId);
        params.put("parentProcDefId", parentProcDefId);
        return bpmNodeSetDao.getScopeByNodeIdAndProcDefId(params);
    }

    /**
     * 更新节点的选择器范围
     * @param setId 节点id
     * @param scope 选择器范围
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateScopeById(String setId, String scope) {
        if(StringUtil.isEmpty(setId)){
            return;
        }
        bpmNodeSetDao.updateScopeById(setId,scope);
    }

    /**
     * 根据流程定义ID获取绑定表单的formKey
     * @param procDefId 流程定义id
     * @return formKey
     */
    public String getFormKeyByProcDefId(String procDefId) {
        return  bpmNodeSetDao.getFormKeyByProcDefId(procDefId);
    }

    public BpmNodeSet getByProcDefIdJoinTaskKey(String procDefId, String joinTaskKey) {
        return bpmNodeSetDao.getByProcDefIdJoinTaskKey(procDefId,joinTaskKey);
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateJoinTask(String setId, Short nodeType, String joinTaskKey, String joinTaskName) {
        Map<String,Object> params = new HashMap<>(4);
        params.put("setId",setId);
        params.put("nodeType",nodeType);
        params.put("joinTaskKey",joinTaskKey);
        params.put("joinTaskName",joinTaskName);
        bpmNodeSetDao.updateJoinTask(params);
    }


    /**
     * 从节点数据中获取子流程的表单
     * @param procDefId 流程定义id
     * @param parentProcDefId 父流程定义id
     * @return BpmNodeSet
     */
    public BpmNodeSet getStartBpmNodeSet(String procDefId, String parentProcDefId) {
        FlowNodeInfo flowNodeInfo = null;
        try {
            flowNodeInfo = NodeCache.getFirstNodeId(procDefId);
        } catch (Exception e) {
            e.printStackTrace();
        }

        String nodeId = "";
        if (flowNodeInfo != null) {
            nodeId = flowNodeInfo.getNodeId();
        }
        BpmNodeSet firstNodeSet = getByProcDefIdNodeIdOrParentProcDefId(nodeId, procDefId, parentProcDefId);
        if (firstNodeSet != null && !BpmConst.FORM_TYPE_NULL .equals(firstNodeSet.getFormType())) {
            return firstNodeSet;
        }

        BpmNodeSet globalNodeSet = bpmNodeSetDao.getGlobalByParent(procDefId, parentProcDefId);
        if (globalNodeSet != null && !BpmConst.FORM_TYPE_NULL .equals(globalNodeSet.getFormType())) {
            return globalNodeSet;
        }

        return null;
    }

    /**
     * 根据流程定义ID获取绑定表单的formKey
     * @param procDefId 流程定义id
     * @param parentProcDefId 父流程定义id
     * @return formKey
     */
    public String getFormKeyByProcDefIdAndParent(String procDefId, String parentProcDefId) {
        return bpmNodeSetDao.getFormKeyByProcDefIdAndParent(procDefId, parentProcDefId);
    }
}
